package com.springcloud.generate.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.springcloud.generate.config.Config;
import com.springcloud.generate.config.SysCodeGenerateConfig;
import com.springcloud.generate.context.XnVelocityContext;
import com.springcloud.generate.entity.CodeGenerate;
import com.springcloud.generate.enums.QueryTypeEnum;
import com.springcloud.generate.enums.TableFilteredFieldsEnum;
import com.springcloud.generate.enums.YesOrNotEnum;
import com.springcloud.generate.param.CodeGenerateParam;
import com.springcloud.generate.param.XnCodeGenParam;
import com.springcloud.generate.result.InforMationColumnsResult;
import com.springcloud.generate.tool.JavaEffTool;
import com.springcloud.generate.tool.JavaSqlTool;
import com.springcloud.generate.tool.NamingConTool;
import com.springcloud.generate.tool.StringDateTool;
import com.springcloud.generate.utils.JdbcUtils;
import com.springcloud.generate.utils.PropertiesUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.velocity.app.VelocityEngine;

import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class CodeGenerateService {

    private static final Logger logger = LogManager.getLogger(CodeGenerateService.class);
    /**
     * 模板后缀
     */
    private static String TEMP_SUFFIX = ".vm";

    /**
     * 转换的编码
     */
    private static String ENCODED = "UTF-8";

    /**
     * 转换模板名称所需变量
     */
    private static String ADD_FORM_PAGE_NAME = "addForm.vue";
    private static String EDIT_FORM_PAGE_NAME = "editForm.vue";
    private static String INDEX_PAGE_NAME = "index.vue";
    private static String MANAGE_JS_NAME = "Manage.js";
    private static String SQL_NAME = ".sql";
    private static String JAVA_SUFFIX = ".java";
    private static String TEMP_ENTITY_NAME = "entity";

    public void runLocal(CodeGenerateParam codeGenerateParam) {
        XnCodeGenParam xnCodeGenParam = copyParams(codeGenerateParam);
        codeGenLocal(xnCodeGenParam);
    }

    /**
     * 本地项目生成
     */
    private void codeGenLocal(XnCodeGenParam xnCodeGenParam) {
        XnVelocityContext context = new XnVelocityContext();
        //初始化参数
        Properties properties = new Properties();
        //设置velocity资源加载方式为class
        properties.setProperty("resource.loader", "class");
        //设置velocity资源加载方式为file时的处理类
        properties.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        //实例化一个VelocityEngine对象
        VelocityEngine velocityEngine = new VelocityEngine(properties);

        String[] filePath = Config.xnCodeGenFilePath(xnCodeGenParam.getBusName(), xnCodeGenParam.getPackageName());

        int index = filePath.length;
        boolean isGenerateSql =  Boolean.parseBoolean(PropertiesUtil.getProperties("isGenerateSql"));
        if(!isGenerateSql){
            index = index - 2;
        }

        for (int i = 0; i < index; i++) {
            String templateName = Config.xnCodeGenTempFile[i];

            String fileBaseName = ResetFileBaseName(xnCodeGenParam.getClassName(),
                    templateName.substring(templateName.indexOf(Config.FILE_SEP) + 1, templateName.lastIndexOf(TEMP_SUFFIX)));
            String path = Config.getLocalPath();
            // 前端VUE位置有所变化, sql同样根目录
            if (fileBaseName.contains(INDEX_PAGE_NAME) || fileBaseName.contains(ADD_FORM_PAGE_NAME) ||
                    fileBaseName.contains(EDIT_FORM_PAGE_NAME) || fileBaseName.contains(MANAGE_JS_NAME) ||
                    fileBaseName.contains(SQL_NAME)) {
                path = Config.getLocalFrontPath();
            }

            File file = new File(path + filePath[i] + fileBaseName);

            //判断是否覆盖存在的文件
            if (file.exists() && !Config.FLAG) {
                continue;
            }

            //获取父目录
            File parentFile = file.getParentFile();
            if (!parentFile.exists()) {
                parentFile.mkdirs();
            }
            try {
                Writer writer = new FileWriter(file);
                velocityEngine.mergeTemplate(Config.templatePath + templateName, ENCODED, context.createVelContext(xnCodeGenParam), writer);
                writer.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private XnCodeGenParam copyParams(CodeGenerateParam codeGenerateParam) {
        CodeGenerate codeGenerate = getCodeGenerate(codeGenerateParam);
        List<SysCodeGenerateConfig> configList = getSysCodeGenerateConfigList(codeGenerate, codeGenerateParam.getDbName(), codeGenerateParam.getTableName());

        XnCodeGenParam param = new XnCodeGenParam();
        BeanUtil.copyProperties(codeGenerate, param);
        // 功能名
        param.setFunctionName(codeGenerate.getTableComment());
        param.setConfigList(configList);
        param.setCreateTimeString(StringDateTool.getStringDate());

        return param;
    }

    public CodeGenerate getCodeGenerate(CodeGenerateParam codeGenerateParam) {
        CodeGenerate codeGenerate = new CodeGenerate();

        String sql = "SELECT" +
                " table_comment " +
                "FROM " +
                "information_schema.TABLES " +
                "WHERE " +
                "table_schema = ? " +
                "AND table_name = ?";

        Connection connection = null;
        PreparedStatement ps = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();
            ps = connection.prepareStatement(sql);

            ps.setString(1, codeGenerateParam.getDbName());
            ps.setString(2, codeGenerateParam.getTableName());

            resultSet = ps.executeQuery();
            while (resultSet.next()) {
                String tableComment = resultSet.getString("table_comment");

                codeGenerate.setTableComment(tableComment);
            }

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils.closeResource(connection, ps, resultSet);
        }

        codeGenerate.setAuthorName(codeGenerateParam.getAuthorName());
        codeGenerate.setClassName(codeGenerateParam.getClassName());
        codeGenerate.setTablePrefix(codeGenerateParam.getTablePrefix());
        codeGenerate.setTableName(codeGenerateParam.getTableName());
        codeGenerate.setPackageName(codeGenerateParam.getPackageName());
        codeGenerate.setBusName(codeGenerateParam.getBusName());
        codeGenerate.setGenerateType(codeGenerateParam.getGenerateType());

        return codeGenerate;
    }

    public List<SysCodeGenerateConfig> getSysCodeGenerateConfigList(CodeGenerate codeGenerate, String dbName, String tableName) {
        List<InforMationColumnsResult> inforMationColumnsResultList = getInforMationColumnsResultList(dbName, tableName);

        List<SysCodeGenerateConfig> sysCodeGenerateConfigList = new ArrayList<>();
        for (InforMationColumnsResult inforMationColumnsResult : inforMationColumnsResultList) {
            SysCodeGenerateConfig sysCodeGenerateConfig = new SysCodeGenerateConfig();

            String YesOrNo = YesOrNotEnum.Y.getCode();
            if (ObjectUtil.isNotNull(inforMationColumnsResult.getColumnKey())
                    && inforMationColumnsResult.getColumnKey().equals(Config.DB_TABLE_COM_KRY) ||
                    TableFilteredFieldsEnum.contains(inforMationColumnsResult.getColumnName())) {
                YesOrNo = YesOrNotEnum.N.getCode();
            }
            if (TableFilteredFieldsEnum.contains(inforMationColumnsResult.getColumnName())) {
                sysCodeGenerateConfig.setWhetherCommon(YesOrNotEnum.Y.getCode());
            } else {
                sysCodeGenerateConfig.setWhetherCommon(YesOrNotEnum.N.getCode());
            }

//            sysCodeGenerateConfig.setCodeGenId(codeGenerate.getId());
            sysCodeGenerateConfig.setColumnName(inforMationColumnsResult.getColumnName());
            sysCodeGenerateConfig.setColumnComment(inforMationColumnsResult.getColumnComment());
            sysCodeGenerateConfig.setJavaName(NamingConTool.UnderlineToHump(inforMationColumnsResult.getColumnName(), codeGenerate.getTablePrefix()));
            sysCodeGenerateConfig.setJavaType(JavaSqlTool.sqlToJava(inforMationColumnsResult.getDataType()));
            sysCodeGenerateConfig.setWhetherRetract(YesOrNotEnum.N.getCode());

            sysCodeGenerateConfig.setWhetherRequired(YesOrNo);
            sysCodeGenerateConfig.setQueryWhether(YesOrNo);
            sysCodeGenerateConfig.setWhetherAddUpdate(YesOrNo);
            sysCodeGenerateConfig.setWhetherTable(YesOrNo);

            sysCodeGenerateConfig.setColumnKey(inforMationColumnsResult.getColumnKey());

            // 设置get set方法使用的名称
            String columnName = NamingConTool.UnderlineToHump(sysCodeGenerateConfig.getColumnName(), "");
            sysCodeGenerateConfig.setColumnKeyName(columnName.substring(0, 1).toUpperCase() + columnName.substring(1, columnName.length()));

            sysCodeGenerateConfig.setDataType(inforMationColumnsResult.getDataType());
            sysCodeGenerateConfig.setEffectType(JavaEffTool.javaToEff(sysCodeGenerateConfig.getJavaType()));
            sysCodeGenerateConfig.setQueryType(QueryTypeEnum.eq.getCode());

            sysCodeGenerateConfigList.add(sysCodeGenerateConfig);
        }

        return sysCodeGenerateConfigList;
    }

    public List<InforMationColumnsResult> getInforMationColumnsResultList(String dbName, String tableName) {
        List<InforMationColumnsResult> list = new ArrayList<>();

        String sql = "SELECT " +
                "column_name, " +
                "data_type, " +
                "column_comment, " +
                "column_key " +
                "FROM " +
                "information_schema.COLUMNS  " +
                "WHERE " +
                "table_schema = ? " +
                "AND table_name = ? ";
        Connection connection = null;
        PreparedStatement ps = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();
            ps = connection.prepareStatement(sql);

            ps.setString(1, dbName);
            ps.setString(2, tableName);

            resultSet = ps.executeQuery();

            while (resultSet.next()) {
                String columnName = resultSet.getString("column_name");
                String dataType = resultSet.getString("data_type");
                String columnComment = resultSet.getString("column_comment");
                String columnKey = resultSet.getString("column_key");

                InforMationColumnsResult infoResult = new InforMationColumnsResult();
                infoResult.setColumnName(columnName);
                infoResult.setDataType(dataType);
                infoResult.setColumnComment(columnComment);
                infoResult.setColumnKey(columnKey);

                list.add(infoResult);
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();

            logger.error("数据库出错!");
        } finally {
            JdbcUtils.closeResource(connection, ps, resultSet);
        }
        return list;
    }

    /**
     * 重置文件名称
     */
    private static String ResetFileBaseName(String className, String fileName) {
        String fileBaseName = className + fileName;
        // 实体类名称单独处理
        if (fileBaseName.contains(TEMP_ENTITY_NAME)) {
            return className + JAVA_SUFFIX;
        }
        // 首页index.vue界面
        if (fileBaseName.contains(INDEX_PAGE_NAME)) {
            return INDEX_PAGE_NAME;
        }
        // 表单界面名称
        if (fileBaseName.contains(ADD_FORM_PAGE_NAME)) {
            return ADD_FORM_PAGE_NAME;
        }
        if (fileBaseName.contains(EDIT_FORM_PAGE_NAME)) {
            return EDIT_FORM_PAGE_NAME;
        }
        // js名称
        if (fileBaseName.contains(MANAGE_JS_NAME)) {
            return className.substring(0, 1).toLowerCase() + className.substring(1) + MANAGE_JS_NAME;
        }
        return fileBaseName;
    }

    /**
     * 获取代码生成基础配置
     *
     * @author yubaoshan
     * @date 2020年12月16日21:19:10
     */
    private CodeGenerate queryCodeGenerate(CodeGenerateParam codeGenerateParam) {
        CodeGenerate generate = new CodeGenerate();
        BeanUtil.copyProperties(generate, codeGenerateParam);

        return new CodeGenerate();
    }
}
